﻿using Dynamics.NAV.Metro.Common;
using System;
using System.Collections.Generic;
using System.Composition;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using Windows.Security.Credentials;
using Windows.Web.AtomPub;
using Windows.Web.Syndication;

namespace Dynamics.NAV.Metro.Data
{
    [Export(typeof(IDynamicsNavApi))]
    [Shared]
    public class DynamicsNavApi : IDynamicsNavApi
    {
        private Uri _rootUri;
        private string _user;
        private string _password;
        private AtomPubClient client;
        private List<NavCompany> _companies = new List<NavCompany>();
        private bool _connected = false;

        public DynamicsNavApi()
        {
            Connect(true);
        }

        public bool Connected
        {
            get { return _connected; }
        }

        public IList<NavCompany> Companies
        {
            get { return _companies; }
        }

        public NavCompany CurrentCompany { get; set; }

        public bool Connect(bool reconnect)
        {
            if (reconnect || !_connected)
            {
                _connected = false;

                if (Uri.TryCreate(SettingsStorage.GetLocalSetting<string>(App.DEFAULT_URL_KEY), UriKind.RelativeOrAbsolute, out _rootUri))
                {
                    _user = SettingsStorage.GetLocalSetting<string>(App.DEFAULT_USERNAME_KEY);
                    _password = SettingsStorage.GetLocalSetting<string>(App.DEFAULT_PASSWORD_KEY);

                    try
                    {
                        _connected = true;
                        NavCompanyPage result = AsyncHelpers.RunSync<NavCompanyPage>(() => this.GetCompanies());
                        _companies = new List<NavCompany>(result.Companies.ToArray());

                        CurrentCompany = null;
                        string company = SettingsStorage.GetLocalSetting<string>(App.DEFAULT_COMPANY_KEY);
                        if (!string.IsNullOrWhiteSpace(company))
                        {
                            foreach (NavCompany comp in Companies)
                            {
                                if (comp.Name == company)
                                {
                                    CurrentCompany = comp;
                                    break;
                                }
                            }
                        }

                        if (CurrentCompany == null)
                        {
                            CurrentCompany = Companies.FirstOrDefault();
                            SettingsStorage.SetLocalSetting(App.DEFAULT_COMPANY_KEY, CurrentCompany.Name);
                        }
                    }
                    catch 
                    {
                        _connected = false;
                    }
                }
            }

            return _connected;
        }

        private AtomPubClient GetClient()
        {
            if (client == null)
            {
                client = new AtomPubClient();
                client.BypassCacheOnRetrieve = true;
                if (!String.IsNullOrEmpty(_user) && !String.IsNullOrEmpty(_password))
                {
                    client.ServerCredential = new PasswordCredential()
                    {
                        UserName = _user,
                        Password = _password
                    };
                }
                else
                {
                    client.ServerCredential = null;
                }
            }

            return client;
        }

        public async Task<int> GetItemCount(int maxCount, string serviceName, string filter)
        {
            if (Connected)
            {
                int currentIndex = maxCount;

                while (true)
                {
                    string token = string.Format("?$top={0}&$skip={1}", 1, currentIndex);
                    if (!string.IsNullOrEmpty(filter))
                        token = string.Format("{0}&$filter={1}", token, filter);
                    Uri resourceUri = new Uri(_rootUri.ToString() + string.Format("Company('{0}')/", CurrentCompany.Name) + serviceName + token);
                    SyndicationFeed feed = await GetClient().RetrieveFeedAsync(resourceUri);
                    if (feed.Items.Count == 0)
                    {
                        currentIndex -= maxCount;
                        token = string.Format("?$skip={0}", currentIndex);
                        if (!string.IsNullOrEmpty(filter))
                            token = string.Format("{0}&$filter={1}", token, filter);
                        resourceUri = new Uri(_rootUri.ToString() + string.Format("Company('{0}')/", CurrentCompany.Name) + serviceName + token);
                        feed = await GetClient().RetrieveFeedAsync(resourceUri);
                        currentIndex += feed.Items.Count;
                        return currentIndex;
                    }
                    else
                    {
                        currentIndex += maxCount;
                    }
                }
            }
            else
                return default(int);
        }

        public async Task<T> GetItems<T>(int index, int perPage, string serviceName, string filter)
        {
            if (Connected)
            {
                string token = string.Format("?$top={0}&$skip={1}", perPage, index);
                if (!string.IsNullOrEmpty(filter))
                    token = string.Format("{0}&$filter={1}", token, filter);
                Uri resourceUri = new Uri(_rootUri.ToString() + string.Format("Company('{0}')/", CurrentCompany.Name) + serviceName + token);

                XDocument responseDocument = XDocument.Parse("<entries />");

                SyndicationFeed feed = await GetClient().RetrieveFeedAsync(resourceUri);
                foreach (SyndicationItem item in feed.Items)
                {
                    XElement ele = XElement.Parse(item.Content.Xml.FirstChild.GetXml());
                    ele.DescendantsAndSelf().Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
                    var all = ele.DescendantsAndSelf();
                    foreach (var el in all)
                        el.Name = el.Name.LocalName;
                    responseDocument.Root.Add(ele);
                }

                XElement rspElement = responseDocument.Root;

                XmlSerializer serializer = new XmlSerializer(typeof(T));
                using (XmlReader reader = rspElement.CreateReader())
                {
                    object result = serializer.Deserialize(reader);
                    return (T)result;
                }
            }
            else
                return default(T);
        }

        public async Task<T> GetItems<T>(int index, string serviceName, string filter)
        {
            if (Connected)
            {
                string token = string.Format("?&$skip={0}", index);
                if (!string.IsNullOrEmpty(filter))
                    token = string.Format("{0}&$filter={1}", token, filter);
                Uri resourceUri = new Uri(_rootUri.ToString() + string.Format("Company('{0}')/", CurrentCompany.Name) + serviceName + token);

                XDocument responseDocument = XDocument.Parse("<entries />");

                SyndicationFeed feed = await GetClient().RetrieveFeedAsync(resourceUri);
                foreach (SyndicationItem item in feed.Items)
                {
                    XElement ele = XElement.Parse(item.Content.Xml.FirstChild.GetXml());
                    ele.DescendantsAndSelf().Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
                    var all = ele.DescendantsAndSelf();
                    foreach (var el in all)
                        el.Name = el.Name.LocalName;
                    responseDocument.Root.Add(ele);
                }

                XElement rspElement = responseDocument.Root;

                XmlSerializer serializer = new XmlSerializer(typeof(T));
                using (XmlReader reader = rspElement.CreateReader())
                {
                    object result = serializer.Deserialize(reader);
                    return (T)result;
                }
            }
            else
                return default(T);
        }
    
        public async Task<NavCompanyPage> GetCompanies()
        {
            Uri resourceUri = new Uri(_rootUri.ToString() + "Company");

            XDocument responseDocument = XDocument.Parse("<entries />");

            SyndicationFeed feed = await GetClient().RetrieveFeedAsync(resourceUri);
            foreach (SyndicationItem item in feed.Items)
            {
                XElement ele = XElement.Parse(item.Content.Xml.FirstChild.GetXml());
                ele.DescendantsAndSelf().Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
                var all = ele.DescendantsAndSelf();
                foreach (var el in all)
                    el.Name = el.Name.LocalName;
                responseDocument.Root.Add(ele);
            }

            XElement rspElement = responseDocument.Root;

            XmlSerializer serializer = new XmlSerializer(typeof(NavCompanyPage));
            using (XmlReader reader = rspElement.CreateReader())
            {
                object result = serializer.Deserialize(reader);
                return (NavCompanyPage)result;
            }
        }
    }
}

